@tycoworks/tycoslide 0.15.1 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/dist/agents/catalog.d.ts +32 -0
- package/dist/agents/catalog.js +37 -0
- package/dist/agents/commands.d.ts +9 -0
- package/dist/agents/commands.js +57 -0
- package/dist/agents/extract.d.ts +22 -0
- package/dist/agents/extract.js +54 -0
- package/dist/{files.d.ts → agents/files.d.ts} +14 -3
- package/dist/{files.js → agents/files.js} +20 -5
- package/dist/agents/inventory.d.ts +83 -0
- package/dist/agents/inventory.js +568 -0
- package/dist/agents/manifest.d.ts +3 -0
- package/dist/{manifest.js → agents/manifest.js} +3 -14
- package/dist/agents/media.d.ts +38 -0
- package/dist/agents/media.js +83 -0
- package/dist/agents/pptx.d.ts +48 -0
- package/dist/agents/pptx.js +123 -0
- package/dist/agents/skill.d.ts +43 -0
- package/dist/agents/skill.js +134 -0
- package/dist/cli.js +8 -44
- package/dist/engine/dom.d.ts +3 -0
- package/dist/engine/dom.js +4 -0
- package/dist/engine/fillers/image.d.ts +6 -5
- package/dist/engine/fillers/image.js +24 -7
- package/dist/engine/generate.d.ts +2 -2
- package/dist/engine/generate.js +2 -2
- package/dist/engine/index.d.ts +3 -0
- package/dist/engine/index.js +2 -0
- package/dist/engine/notes.js +20 -62
- package/dist/engine/ooxml.d.ts +36 -0
- package/dist/engine/ooxml.js +56 -0
- package/dist/engine/types.d.ts +4 -0
- package/dist/index.d.ts +4 -11
- package/dist/index.js +2 -15
- package/dist/markdown/blocks/code.js +1 -2
- package/dist/markdown/blocks/image.d.ts +23 -0
- package/dist/markdown/blocks/image.js +65 -3
- package/dist/markdown/blocks/mermaid.d.ts +1 -1
- package/dist/markdown/blocks/mermaid.js +6 -4
- package/dist/markdown/blocks/table.js +7 -6
- package/dist/markdown/blocks/text.js +9 -8
- package/dist/markdown/deckCompiler.d.ts +2 -13
- package/dist/markdown/deckCompiler.js +7 -78
- package/dist/markdown/index.d.ts +4 -2
- package/dist/markdown/index.js +3 -1
- package/dist/markdown/inline.d.ts +2 -0
- package/dist/markdown/inline.js +4 -3
- package/dist/markdown/schema/themeConfigSchema.d.ts +2 -11
- package/dist/markdown/schema/themeConfigSchema.js +6 -16
- package/dist/markdown/slideParser.d.ts +2 -0
- package/dist/markdown/slideParser.js +6 -3
- package/dist/markdown/types.d.ts +4 -29
- package/dist/markdown/types.js +0 -11
- package/{theme-package/syntax.md → docs/markdown.md} +60 -28
- package/docs/theme.md +144 -0
- package/package.json +5 -4
- package/theme-package/SKILL.template.md +161 -0
- package/dist/engine/rels.d.ts +0 -18
- package/dist/engine/rels.js +0 -55
- package/dist/manifest.d.ts +0 -5
- package/dist/skillZip.d.ts +0 -49
- package/dist/skillZip.js +0 -177
- package/theme-package/SKILL.md +0 -226
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tycoslide
|
|
2
2
|
|
|
3
|
-
Create editable
|
|
3
|
+
Create editable PowerPoint slides from markdown, using your existing .pptx templates.
|
|
4
4
|
|
|
5
5
|
## Getting started
|
|
6
6
|
|
|
@@ -18,17 +18,21 @@ npm install
|
|
|
18
18
|
npx tycoslide build how-it-works.md
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
https://github.com/user-attachments/assets/f31c11d8-3fe6-464f-8f02-56645b4ce3a6
|
|
22
|
+
|
|
21
23
|
## Markdown support
|
|
22
24
|
|
|
23
25
|
- Paragraphs, bullets and numbered lists, with bold, italic and links
|
|
24
|
-
- Tables
|
|
26
|
+
- Tables, images and speaker notes
|
|
25
27
|
- Code, with [Shiki](https://shiki.style) syntax highlighting
|
|
26
28
|
- [Mermaid](https://mermaid.js.org) diagrams
|
|
27
|
-
- Images, from the theme's asset catalog or a file path
|
|
28
|
-
- Speaker notes
|
|
29
29
|
|
|
30
|
-
Full syntax in [
|
|
30
|
+
Full syntax in [markdown.md](docs/markdown.md).
|
|
31
31
|
|
|
32
32
|
## Requirements
|
|
33
33
|
|
|
34
34
|
Node 23.6 or later. Mermaid diagrams need Chrome on the machine; tycoslide finds an installed one, or run `npx playwright install chromium-headless-shell`.
|
|
35
|
+
|
|
36
|
+
## Thanks
|
|
37
|
+
|
|
38
|
+
tycoslide was inspired by [pptx-automizer](https://github.com/singerla/pptx-automizer), which it uses under the hood, and by [Slidev](https://sli.dev) and its markdown slides.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* One image a theme offers deck authors. `fit` is the value an author copies
|
|
4
|
+
* into the image's title, so it is already a title value.
|
|
5
|
+
*/
|
|
6
|
+
declare const AssetEntrySchema: z.ZodObject<{
|
|
7
|
+
path: z.ZodString;
|
|
8
|
+
fit: z.ZodEnum<{
|
|
9
|
+
readonly Contain: "contain";
|
|
10
|
+
readonly Cover: "cover";
|
|
11
|
+
readonly ScaleDown: "scale-down";
|
|
12
|
+
}>;
|
|
13
|
+
description: z.ZodString;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
declare const AssetCatalogSchema: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
16
|
+
path: z.ZodString;
|
|
17
|
+
fit: z.ZodEnum<{
|
|
18
|
+
readonly Contain: "contain";
|
|
19
|
+
readonly Cover: "cover";
|
|
20
|
+
readonly ScaleDown: "scale-down";
|
|
21
|
+
}>;
|
|
22
|
+
description: z.ZodString;
|
|
23
|
+
}, z.core.$strict>>>;
|
|
24
|
+
export type AssetEntry = z.infer<typeof AssetEntrySchema>;
|
|
25
|
+
export type AssetCatalog = z.infer<typeof AssetCatalogSchema>;
|
|
26
|
+
/**
|
|
27
|
+
* Read and validate a theme's image catalog, `assets.json`: what a deck-writing
|
|
28
|
+
* agent searches, copies images from, and takes each image's fit from. It is
|
|
29
|
+
* authored with the theme and required; a theme with no images has `{}`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadAssetCatalog(themeDir: string): AssetCatalog;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
import { ImageFit, strict } from "../index.js";
|
|
5
|
+
import { ASSETS_FILE } from "./files.js";
|
|
6
|
+
/**
|
|
7
|
+
* One image a theme offers deck authors. `fit` is the value an author copies
|
|
8
|
+
* into the image's title, so it is already a title value.
|
|
9
|
+
*/
|
|
10
|
+
const AssetEntrySchema = strict({
|
|
11
|
+
path: z.string(),
|
|
12
|
+
fit: z.enum(ImageFit),
|
|
13
|
+
description: z.string(),
|
|
14
|
+
});
|
|
15
|
+
// `{ category: { name: entry } }`. Both record levels are open (the names are the
|
|
16
|
+
// theme's own); only the entry is strict.
|
|
17
|
+
const AssetCatalogSchema = z.record(z.string(), z.record(z.string(), AssetEntrySchema));
|
|
18
|
+
/**
|
|
19
|
+
* Read and validate a theme's image catalog, `assets.json`: what a deck-writing
|
|
20
|
+
* agent searches, copies images from, and takes each image's fit from. It is
|
|
21
|
+
* authored with the theme and required; a theme with no images has `{}`.
|
|
22
|
+
*/
|
|
23
|
+
export function loadAssetCatalog(themeDir) {
|
|
24
|
+
const path = join(themeDir, ASSETS_FILE);
|
|
25
|
+
let raw;
|
|
26
|
+
try {
|
|
27
|
+
raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new Error(`Image catalog not found or invalid JSON: ${path}`);
|
|
31
|
+
}
|
|
32
|
+
const result = AssetCatalogSchema.safeParse(raw);
|
|
33
|
+
if (!result.success) {
|
|
34
|
+
throw new Error(`${ASSETS_FILE}: invalid image catalog\n${z.prettifyError(result.error)}`);
|
|
35
|
+
}
|
|
36
|
+
return result.data;
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
/** The installed tycoslide package: where its shipped files live, and what a skill pins. */
|
|
3
|
+
export type ToolPackage = {
|
|
4
|
+
root: string;
|
|
5
|
+
name: string;
|
|
6
|
+
version: string;
|
|
7
|
+
};
|
|
8
|
+
/** Register the agent layer's commands on the CLI program. */
|
|
9
|
+
export declare function registerAgentCommands(program: Command, tool: ToolPackage): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, posix, resolve } from "node:path";
|
|
3
|
+
import { loadThemeConfig } from "../index.js";
|
|
4
|
+
import { loadAssetCatalog } from "./catalog.js";
|
|
5
|
+
import { extractTemplate, summarizeExtraction } from "./extract.js";
|
|
6
|
+
import { ASSETS_DIR, ASSETS_FILE, MANIFEST_FILE, PACKAGE_JSON, SKILL_FILE, SKILL_TEMPLATE_FILE, SKILL_ZIP_EXT, TEMPLATE_FILE, THEME_CONFIG, THEME_PACKAGE_DIR, } from "./files.js";
|
|
7
|
+
import { generateManifest } from "./manifest.js";
|
|
8
|
+
import { renameSkill, skillPackageJson, zipDir } from "./skill.js";
|
|
9
|
+
/** The CLI's line for something written, with an optional note on what it holds. */
|
|
10
|
+
function wrote(target, detail) {
|
|
11
|
+
console.log(detail ? `WROTE ${target} ${detail}` : `WROTE ${target}`);
|
|
12
|
+
}
|
|
13
|
+
/** Register the agent layer's commands on the CLI program. */
|
|
14
|
+
export function registerAgentCommands(program, tool) {
|
|
15
|
+
const skillMdPath = resolve(tool.root, THEME_PACKAGE_DIR, SKILL_TEMPLATE_FILE);
|
|
16
|
+
program
|
|
17
|
+
.command("package")
|
|
18
|
+
.description("Zip the theme into an Agent Skill for AI agents")
|
|
19
|
+
.option(`-c, --config <path>`, "path to theme config file", THEME_CONFIG)
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const config = loadThemeConfig(resolve(process.cwd(), opts.config));
|
|
22
|
+
const catalog = loadAssetCatalog(config.rootDir);
|
|
23
|
+
const themePkg = JSON.parse(readFileSync(resolve(process.cwd(), PACKAGE_JSON), "utf-8"));
|
|
24
|
+
if (!themePkg.name) {
|
|
25
|
+
throw new Error(`Cannot name the skill: the theme's ${PACKAGE_JSON} has no "name" field.`);
|
|
26
|
+
}
|
|
27
|
+
// basename drops any npm scope, e.g. "@acme/acme-slides" -> "acme-slides".
|
|
28
|
+
const skillName = basename(themePkg.name);
|
|
29
|
+
let skillMd;
|
|
30
|
+
try {
|
|
31
|
+
skillMd = renameSkill(readFileSync(skillMdPath, "utf-8"), skillName);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new Error(`${skillMdPath}: ${err.message}`);
|
|
35
|
+
}
|
|
36
|
+
// Bundle the WHOLE theme so the skill is self-contained: unzip ->
|
|
37
|
+
// `npm install` (pulls the engine, its docs and its deps) -> `npx tycoslide build`.
|
|
38
|
+
const generated = {
|
|
39
|
+
[PACKAGE_JSON]: skillPackageJson(themePkg, tool),
|
|
40
|
+
[SKILL_FILE]: skillMd,
|
|
41
|
+
[MANIFEST_FILE]: generateManifest(config),
|
|
42
|
+
};
|
|
43
|
+
const zipFile = `${skillName}${SKILL_ZIP_EXT}`;
|
|
44
|
+
const zip = await zipDir(process.cwd(), skillName, config, catalog, [opts.config, ASSETS_FILE], generated);
|
|
45
|
+
writeFileSync(resolve(process.cwd(), zipFile), zip);
|
|
46
|
+
wrote(zipFile);
|
|
47
|
+
});
|
|
48
|
+
program
|
|
49
|
+
.command("extract")
|
|
50
|
+
.description(`Read a template for a theme: write ${TEMPLATE_FILE}, and copy its images into ${ASSETS_DIR}/`)
|
|
51
|
+
.argument("<template>", "path to the .pptx template")
|
|
52
|
+
.action(async (template) => {
|
|
53
|
+
const summary = summarizeExtraction(await extractTemplate(resolve(process.cwd(), template), process.cwd()));
|
|
54
|
+
wrote(TEMPLATE_FILE, summary.template);
|
|
55
|
+
wrote(ASSETS_DIR + posix.sep, summary.images);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract: read a template for a theme in one pass. Writes the inventory, with the
|
|
3
|
+
* images it copied, to `template.json`, and copies the template's images into
|
|
4
|
+
* `assets/`, both in the theme folder.
|
|
5
|
+
*/
|
|
6
|
+
import { type Inventory } from "./inventory.js";
|
|
7
|
+
import { type MediaImage } from "./media.js";
|
|
8
|
+
export type TemplateImage = MediaImage & {
|
|
9
|
+
/** Where the image is in the theme folder; for a duplicate, the copy that was kept. Absent when missing. */
|
|
10
|
+
path?: string;
|
|
11
|
+
};
|
|
12
|
+
/** The contents of `template.json`. */
|
|
13
|
+
export type TemplateFacts = Inventory & {
|
|
14
|
+
images: TemplateImage[];
|
|
15
|
+
};
|
|
16
|
+
/** Read `templatePath`, write `template.json` and copy the images into `themeDir`. */
|
|
17
|
+
export declare function extractTemplate(templatePath: string, themeDir: string): Promise<TemplateFacts>;
|
|
18
|
+
/** What was written, in a phrase each: the template's facts, and the images. */
|
|
19
|
+
export declare function summarizeExtraction(facts: TemplateFacts): {
|
|
20
|
+
template: string;
|
|
21
|
+
images: string;
|
|
22
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract: read a template for a theme in one pass. Writes the inventory, with the
|
|
3
|
+
* images it copied, to `template.json`, and copies the template's images into
|
|
4
|
+
* `assets/`, both in the theme folder.
|
|
5
|
+
*/
|
|
6
|
+
import { writeFileSync } from "node:fs";
|
|
7
|
+
import { join, posix } from "node:path";
|
|
8
|
+
import { ASSETS_DIR, jsonFile, TEMPLATE_FILE } from "./files.js";
|
|
9
|
+
import { readInventory } from "./inventory.js";
|
|
10
|
+
import { copyImages, MediaOutcome } from "./media.js";
|
|
11
|
+
import { Presentation } from "./pptx.js";
|
|
12
|
+
/** Read `templatePath`, write `template.json` and copy the images into `themeDir`. */
|
|
13
|
+
export async function extractTemplate(templatePath, themeDir) {
|
|
14
|
+
const presentation = await Presentation.open(templatePath);
|
|
15
|
+
const inventory = await readInventory(presentation);
|
|
16
|
+
const images = await copyImages(presentation, join(themeDir, ASSETS_DIR));
|
|
17
|
+
const facts = {
|
|
18
|
+
...inventory,
|
|
19
|
+
images: images.map((image) => {
|
|
20
|
+
const kept = image.outcome === MediaOutcome.Missing ? undefined : (image.duplicateOf ?? image.file);
|
|
21
|
+
return kept ? { ...image, path: posix.join(ASSETS_DIR, kept) } : image;
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
writeFileSync(join(themeDir, TEMPLATE_FILE), jsonFile(facts));
|
|
25
|
+
return facts;
|
|
26
|
+
}
|
|
27
|
+
/** What was written, in a phrase each: the template's facts, and the images. */
|
|
28
|
+
export function summarizeExtraction(facts) {
|
|
29
|
+
const count = (outcome) => facts.images.filter((image) => image.outcome === outcome).length;
|
|
30
|
+
const template = [
|
|
31
|
+
counted(facts.slides.length, "slide"),
|
|
32
|
+
counted(facts.duplicates.length, "duplicate group"),
|
|
33
|
+
counted(facts.embeddedFonts.length, "embedded typeface"),
|
|
34
|
+
];
|
|
35
|
+
// [count, noun, what happened, report even when none]
|
|
36
|
+
const images = [
|
|
37
|
+
[count(MediaOutcome.Copied), "image", "copied", true],
|
|
38
|
+
[count(MediaOutcome.Present), "image", "already present", false],
|
|
39
|
+
[count(MediaOutcome.Duplicate), "identical image", "skipped", false],
|
|
40
|
+
[count(MediaOutcome.Missing), "image", "missing from the template", false],
|
|
41
|
+
];
|
|
42
|
+
return {
|
|
43
|
+
template: template.join(LIST_SEPARATOR),
|
|
44
|
+
images: images
|
|
45
|
+
.filter(([n, , , always]) => always || n > 0)
|
|
46
|
+
.map(([n, noun, state]) => `${counted(n, noun)} ${state}`)
|
|
47
|
+
.join(LIST_SEPARATOR),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const LIST_SEPARATOR = ", ";
|
|
51
|
+
/** `1 slide`, `3 slides`. */
|
|
52
|
+
function counted(n, noun) {
|
|
53
|
+
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
54
|
+
}
|
|
@@ -11,13 +11,24 @@ export declare const ASSETS_FILE = "assets.json";
|
|
|
11
11
|
/**
|
|
12
12
|
* The one archive a packaged theme's declared assets ship inside. Entries are
|
|
13
13
|
* stored at theme-relative POSIX paths and never rewritten: packaging writes
|
|
14
|
-
* them,
|
|
14
|
+
* them, and an agent unzips them. Zip format, deliberately NOT named .zip: the
|
|
15
15
|
* upload host rejects nested .zip entries by extension while accepting .pptx,
|
|
16
16
|
* which is also a zip -- so the archive travels under a neutral name.
|
|
17
17
|
*/
|
|
18
18
|
export declare const ASSETS_ARCHIVE = "assets.dat";
|
|
19
19
|
export declare const SKILL_FILE = "SKILL.md";
|
|
20
|
-
export declare const SYNTAX_FILE = "syntax.md";
|
|
21
20
|
export declare const THEME_PACKAGE_DIR = "theme-package";
|
|
22
|
-
|
|
21
|
+
export declare const SKILL_TEMPLATE_FILE = "SKILL.template.md";
|
|
22
|
+
/**
|
|
23
|
+
* npm's package manifest: the theme's, which names the skill, and the packaged
|
|
24
|
+
* skill's own, which it installs from (authored rather than copied).
|
|
25
|
+
*/
|
|
23
26
|
export declare const PACKAGE_JSON = "package.json";
|
|
27
|
+
/** The packaged skill is one zip, named `<skill name>.zip`. */
|
|
28
|
+
export declare const SKILL_ZIP_EXT = ".zip";
|
|
29
|
+
/** What `tycoslide extract` learns about a template, written in the theme folder. */
|
|
30
|
+
export declare const TEMPLATE_FILE = "template.json";
|
|
31
|
+
/** Where a theme keeps its images, and where `tycoslide extract` copies a template's. */
|
|
32
|
+
export declare const ASSETS_DIR = "assets";
|
|
33
|
+
/** `value` as the contents of a JSON file the agent layer writes: indented, with a final newline. */
|
|
34
|
+
export declare function jsonFile(value: unknown): string;
|
|
@@ -11,7 +11,7 @@ export const ASSETS_FILE = "assets.json";
|
|
|
11
11
|
/**
|
|
12
12
|
* The one archive a packaged theme's declared assets ship inside. Entries are
|
|
13
13
|
* stored at theme-relative POSIX paths and never rewritten: packaging writes
|
|
14
|
-
* them,
|
|
14
|
+
* them, and an agent unzips them. Zip format, deliberately NOT named .zip: the
|
|
15
15
|
* upload host rejects nested .zip entries by extension while accepting .pptx,
|
|
16
16
|
* which is also a zip -- so the archive travels under a neutral name.
|
|
17
17
|
*/
|
|
@@ -20,9 +20,24 @@ export const ASSETS_ARCHIVE = "assets.dat";
|
|
|
20
20
|
// the root of a skill folder, and a case-sensitive filesystem will not find any
|
|
21
21
|
// other.
|
|
22
22
|
export const SKILL_FILE = "SKILL.md";
|
|
23
|
-
|
|
24
|
-
// The folder holding the SKILL.md template copied into each packaged theme, kept
|
|
25
|
-
// out of the repo root so skill installers do not mistake it for this repo's own skill.
|
|
23
|
+
// The folder holding the deck skill that `package` puts in each packaged theme.
|
|
26
24
|
export const THEME_PACKAGE_DIR = "theme-package";
|
|
27
|
-
|
|
25
|
+
// The deck skill as tycoslide ships it, named so skill installers scanning this
|
|
26
|
+
// repo don't offer it as a skill of its own; `package` writes it as SKILL_FILE.
|
|
27
|
+
export const SKILL_TEMPLATE_FILE = "SKILL.template.md";
|
|
28
|
+
/**
|
|
29
|
+
* npm's package manifest: the theme's, which names the skill, and the packaged
|
|
30
|
+
* skill's own, which it installs from (authored rather than copied).
|
|
31
|
+
*/
|
|
28
32
|
export const PACKAGE_JSON = "package.json";
|
|
33
|
+
/** The packaged skill is one zip, named `<skill name>.zip`. */
|
|
34
|
+
export const SKILL_ZIP_EXT = ".zip";
|
|
35
|
+
/** What `tycoslide extract` learns about a template, written in the theme folder. */
|
|
36
|
+
export const TEMPLATE_FILE = "template.json";
|
|
37
|
+
/** Where a theme keeps its images, and where `tycoslide extract` copies a template's. */
|
|
38
|
+
export const ASSETS_DIR = "assets";
|
|
39
|
+
const JSON_INDENT = 2;
|
|
40
|
+
/** `value` as the contents of a JSON file the agent layer writes: indented, with a final newline. */
|
|
41
|
+
export function jsonFile(value) {
|
|
42
|
+
return `${JSON.stringify(value, null, JSON_INDENT)}\n`;
|
|
43
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inventory: what a template holds, in the terms `theme.json` needs. The slide
|
|
3
|
+
* size, the colour and font schemes, the embedded typefaces; each slide's
|
|
4
|
+
* position, layout, background tone and shapes (with frames in EMU, as
|
|
5
|
+
* `theme.json` takes them); and which slides share the same geometry.
|
|
6
|
+
*/
|
|
7
|
+
import { type Frame } from "../index.js";
|
|
8
|
+
import { type Presentation } from "./pptx.js";
|
|
9
|
+
/** The twelve colour-scheme slots, in the order PowerPoint lists them. */
|
|
10
|
+
export declare const SCHEME_SLOTS: readonly ["dk1", "lt1", "dk2", "lt2", "accent1", "accent2", "accent3", "accent4", "accent5", "accent6", "hlink", "folHlink"];
|
|
11
|
+
type SchemeSlot = (typeof SCHEME_SLOTS)[number];
|
|
12
|
+
/** A background's tone, as a layout's `variant` would describe it; `image` for a picture fill. */
|
|
13
|
+
export declare const Background: {
|
|
14
|
+
readonly Light: "light";
|
|
15
|
+
readonly Dark: "dark";
|
|
16
|
+
readonly Image: "image";
|
|
17
|
+
readonly Unknown: "unknown";
|
|
18
|
+
};
|
|
19
|
+
export type Background = (typeof Background)[keyof typeof Background];
|
|
20
|
+
/** Scheme slot → `#RRGGBB`. */
|
|
21
|
+
type SchemeColors = Partial<Record<SchemeSlot, string>>;
|
|
22
|
+
export type ColorScheme = {
|
|
23
|
+
name: string;
|
|
24
|
+
} & SchemeColors;
|
|
25
|
+
export type FontScheme = {
|
|
26
|
+
name: string;
|
|
27
|
+
major?: string;
|
|
28
|
+
minor?: string;
|
|
29
|
+
};
|
|
30
|
+
/** A shape's kind. A fillable shape's kind is the `theme.json` block type that fills it. */
|
|
31
|
+
export declare const ShapeKind: {
|
|
32
|
+
readonly Text: "text";
|
|
33
|
+
readonly Image: "image";
|
|
34
|
+
readonly Table: "table";
|
|
35
|
+
readonly Group: "group";
|
|
36
|
+
readonly Other: "other";
|
|
37
|
+
};
|
|
38
|
+
export type ShapeKind = (typeof ShapeKind)[keyof typeof ShapeKind];
|
|
39
|
+
/** Where a placeholder that doesn't position itself takes its frame from. */
|
|
40
|
+
export declare const FrameSource: {
|
|
41
|
+
readonly Layout: "layout";
|
|
42
|
+
readonly Master: "master";
|
|
43
|
+
};
|
|
44
|
+
export type FrameSource = (typeof FrameSource)[keyof typeof FrameSource];
|
|
45
|
+
export type InventoryShape = {
|
|
46
|
+
/** The shape's name; a group's members are prefixed with the group's name and `/`. */
|
|
47
|
+
name: string;
|
|
48
|
+
kind: ShapeKind;
|
|
49
|
+
/** In EMU, as `theme.json` takes it. */
|
|
50
|
+
frame?: Frame;
|
|
51
|
+
frameFrom?: FrameSource;
|
|
52
|
+
rows?: number;
|
|
53
|
+
cols?: number;
|
|
54
|
+
/**
|
|
55
|
+
* The start of the shape's text, with `¶` between paragraphs and `↵` for line
|
|
56
|
+
* breaks: where a parameter template puts `\n`.
|
|
57
|
+
*/
|
|
58
|
+
text: string;
|
|
59
|
+
};
|
|
60
|
+
export type InventorySlide = {
|
|
61
|
+
/** The number in the part name, `ppt/slides/slideN.xml`: what `theme.json`'s `slideNumber` means. */
|
|
62
|
+
slide: number;
|
|
63
|
+
/** 1-based position in presentation order (how rendered previews are numbered); absent when not shown. */
|
|
64
|
+
position?: number;
|
|
65
|
+
layout?: string;
|
|
66
|
+
background: Background;
|
|
67
|
+
shapes: InventoryShape[];
|
|
68
|
+
};
|
|
69
|
+
export type Inventory = {
|
|
70
|
+
slideSize: {
|
|
71
|
+
cx: number;
|
|
72
|
+
cy: number;
|
|
73
|
+
};
|
|
74
|
+
colorScheme: ColorScheme;
|
|
75
|
+
fontScheme: FontScheme;
|
|
76
|
+
embeddedFonts: string[];
|
|
77
|
+
slides: InventorySlide[];
|
|
78
|
+
/** Groups of slide numbers whose shapes have the same kinds and frames, to within a point. */
|
|
79
|
+
duplicates: number[][];
|
|
80
|
+
};
|
|
81
|
+
/** Read a template's inventory. */
|
|
82
|
+
export declare function readInventory(presentation: Presentation): Promise<Inventory>;
|
|
83
|
+
export {};
|